#!/usr/bin/python
#***************************************************************************
# Copyright notice for this file:
#  Copyright (C) 2012 TheAxeMan
#  _______ _                               _    _
# |__   __| |     ___     /\          ___ | \  / |
#    | |  | |__  / _ \   /  \  __  _ / _ \|  \/  | __ _ _ __
#    | |  | '_ \|  __/  / /\ \ \ \/ |  __/| .  . |/ _` | '_ \
#    | |  | | | | (__/\/ ____ \ )  (| (__/| |\/| | (_| | | | |
#    |_|  |_| |_|\____/_/    \_._/\_.\____|_|  |_|\__,_|_| |_|
#
# Final Fantasy TAS scripts by TheAxeMan
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
#***************************************************************************


class CharType:
  """Really just a namespace for character types"""
  garland = 1
  pirate = 2
  wizard = 3
  level1fighter = 80
  level2fighter = 81
  level3fighter = 82


class BattleRandGenerator:
  #this list was pulled from emulator memory viewer
  temp_rand_list = """
   AE D0 38 8A ED 60 DB 72 5C 59 27 D8 0A 4A F4
34 08 A9 C3 96 56 3B F1 55 F8 6B 31 EF 6D 28 AC
41 68 1E 2A C1 E5 8F 50 F5 3E 7B B7 4C 14 39 12
CD B2 62 8B 82 3C BA 63 85 3A 17 B8 2E B5 BE 20
CB 46 51 2C CF 03 78 53 97 06 69 EB 77 86 E6 EA
74 0C 21 E2 40 D4 5A 3D C7 2B 94 D5 8C 44 FD EE
D2 43 00 BB FA C6 1D 98 A0 D3 54 5F 5E DC A8 00
AF 93 A1 E1 6C 04 DE B6 D7 36 16 C5 C8 C4 E4 0F
02 AB E8 33 99 73 11 6A 09 67 F3 FF A2 DF 32 0E
1F 0D 90 25 64 75 B3 65 2F C9 B0 DA 5D 9F EC 29
CE E3 F0 91 7A 58 45 24 1C 47 A4 89 18 2D CC BD
6F 80 F6 81 22 E9 07 70 FB DD AD 35 A6 61 B4 A3
FE B1 30 4B 15 48 6E 4F 5B 13 9C 83 92 01 C2 19
7F 1A 1B 71 B9 3F 4E 9B BF 9E 87 0B 10 57 F2 26
79 9A 05 C0 E0 F7 4D 7D CA 52 9D F9 BC AA FC 8D
7E D1 A5 42 E7 D6 76 A7 84 8E 66 7C 23 88 37 49
D9
""".split()
  rand_list = []
  for i in range(len(temp_rand_list)):
    rand_list.append(int(temp_rand_list[i], 16))

  def __init__(self):
    self.seed = 0

  def copy(self):
    c = BattleRandGenerator()
    c.seed = self.seed
    return c

  def getNextRand(self):
    """Get the next rand from the list and increment seed."""
    r = self.rand_list[self.seed]
    if self.seed >= 255:
      self.seed = 0
    else:
      self.seed += 1
    return r

  def useUpRands(self, num):
    """Equivalent to getting and throwing away a number of rands."""
    self.seed += num
    while(self.seed > 255):
      self.seed -= 256

  def getRandInRange(self, min, max):
    """Returns a rand in the range min to max."""
    r = self.getNextRand()
    rand_range = 1 + max - min
    return min + (r * rand_range) / 256

  def getBattleOrder(self):
    """Use the randomization engine provided to get the battle order"""
    #start out like this, with enemies as 0-9 and party 80-83
    battle_order = [0,1,2,3,4,5,6,7,8,80,81,82,83]
    for i in range(17):
      swap_a = self.getRandInRange(0,12)
      swap_b = self.getRandInRange(0,12)
      #print "swap",swap_a,"and",swap_b
      temp = battle_order[swap_a]
      battle_order[swap_a] = battle_order[swap_b]
      battle_order[swap_b] = temp
    return battle_order

  def luckRaiseSeeds(self):
    """Returns a list of seeds that raise luck on levelup.
       Note that this is the seed that has to come up on the third
       roll in the levelup sequence.
    """
    lucky_seeds = []
    for i in range(256):
      if self.rand_list[i] % 4 == 0:
        lucky_seeds.append(i)
    return lucky_seeds

def rand_test():
  rand_engine = BattleRandGenerator()

  #print "rand list has",len(rand_engine.rand_list),"entries"
  #print rand_engine.rand_list

  rl_copy = rand_engine.rand_list[:]
  rl_copy.sort()
  #print "sorted:",rl_copy

  for x in range(100):
    #print rand_engine.getNextRand()
    print rand_engine.getRandInRange(0,200)

class Combatant:
  """Could be a monster or hero!"""
  def __init__(self):
    self.damage = 10
    self.defense = 0
    self.num_hits = 1
    self.crit_threshold = 0
    self.hp = 35
    self.status = 0
    self.type = 0
    self.hit_thresholds = 0

  def copy(self):
    c = Combatant()
    c.damage = self.damage
    c.defense = self.defense
    c.num_hits = self.num_hits
    c.crit_threshold = self.crit_threshold
    c.hp = self.hp
    c.status = self.status
    c.type = self.type
    #hit_thresholds is a hash, so we are creating a mutable reference...
    #but it should be all right since it's generally static once defined
    c.hit_thresholds = self.hit_thresholds
    return c

  def takeDamage(self, damage):
    """Update hp and status after taking damage"""
    self.hp -= damage
    if self.hp <= 0:
      #dead...
      self.status = 1

  def attack(self, target, rand_engine, print_results=False):
    total_damage = 0
    for i in range(self.num_hits):
      hit_roll = rand_engine.getRandInRange(0, 200)
      damage_roll = rand_engine.getRandInRange(0, self.damage)
      if hit_roll > self.hit_thresholds[target.type]:
        # this is a miss
        continue
      unmodified_damage = damage_roll + self.damage
      hit_damage = unmodified_damage - target.defense
      if hit_damage < 1:
        # as long as it's a hit, there's always at least one damage
        hit_damage = 1
      if hit_roll <= self.crit_threshold:
        # critical hit!
        if print_results:
          print "critical hit!"
        hit_damage += unmodified_damage
      # accumulate hit damage
      total_damage += hit_damage

    if print_results:
      if total_damage == 0:
        print "missed!"
      else:
        print total_damage, "damage"
    #update the target's hp and status
    target.takeDamage(total_damage)
    #return the damage so it can be tracked easily
    return total_damage

